/*
 * MessageHeader.java    0.0.1    27/set/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.net.cs.protocol;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.StreamCorruptedException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is a convenience class used to serialise and deserialise the
 * header of a {@link NIOObjectContainer} instance, hiding the details of how an
 * header is built.
 * 
 * @version 0.0.1 27/set/2009
 * @author Andrea Reale <andrea.reale@gmail.com>
 */
public final class MessageHeader {
    private final static transient Logger log = LoggerFactory
	    .getLogger(NIOObjectContainer.class);
    /**
     * The size in bytes of the header of the externalised form of the container
     */
    public final static int HEADER_SIZE = (Integer.SIZE * 2) / 8 + 1;
    /**
     * A 4 bytes code delimiting the start of a new <tt>NIOObjectContainer</tt>
     * serialisation stream. <br />
     * It currently is: <code>0xDEADCAFE</code>
     */
    public final static int START_MARKER = 0xDEADCAFE;

    private int payloadLength;
    public byte compressed = 0;

    protected MessageHeader() {

    }

    public MessageHeader(byte[] payLoad) {
	setPayloadLength(payLoad.length);
    }

    /**
     * Returns the length in bytes of the serialised representation of the
     * payload object
     * 
     * @return the length in bytes of the serialised representation of the
     *         payload object
     */
    public int getPayloadLength() {
	return payloadLength;
    }

    /**
     * Sets the length of the payload of this <tt>NIOObjectContainer</tt>. It
     * must be non-negative.
     * 
     * @param payloadLength
     *            the length of the payload of this <tt>NIOObjectContainer</tt>.
     */
    public void setPayloadLength(int payloadLength) {
	if (payloadLength < 0) {
	    throw new IllegalArgumentException(
		    "Payload size must be non-negative");
	}

	this.payloadLength = payloadLength;
    }

    public boolean isCompressedPayload() {
	return (compressed != 0);
    }

    public void setCompressedPayload(boolean compressed) {
	if (compressed) {
	    this.compressed = 1;
	} else {
	    this.compressed = 0;
	}
    }

    public byte[] writeHeader() throws IOException {
	ByteArrayOutputStream os = null;
	DataOutputStream dos = null;
	byte[] encodedHeader = null;

	try {
	    os = new ByteArrayOutputStream(HEADER_SIZE);
	    dos = new DataOutputStream(os);
	    dos.writeInt(START_MARKER);
	    dos.writeInt(getPayloadLength());
	    dos.writeByte(this.compressed);
	    dos.flush();
	    encodedHeader = os.toByteArray();
	} finally {
	    os.close();
	    dos.close();
	}
	return encodedHeader;
    }

    public static MessageHeader readHeader(byte[] encodedHeader)
	    throws IOException {
	DataInputStream dis = null;
	int startMarker;
	int payloadLength;
	byte compressedPayload;
	MessageHeader toReturn;

	if (encodedHeader.length != HEADER_SIZE) {
	    String msg = "The encoded header must be " + HEADER_SIZE
		    + " bytes long";
	    log.error(msg);
	    throw new IllegalArgumentException(msg);
	}

	try {
	    dis = new DataInputStream(new ByteArrayInputStream(encodedHeader));
	    startMarker = dis.readInt();
	    if (startMarker != MessageHeader.START_MARKER) {
		String msg = "No start marker was found";
		log.error(msg);
		throw new StreamCorruptedException("No start marker was found");
	    }
	    payloadLength = dis.readInt();
	    compressedPayload = dis.readByte();
	} finally {
	    dis.close();
	}

	toReturn = new MessageHeader();
	toReturn.setPayloadLength(payloadLength);
	if (compressedPayload != 0) {
	    toReturn.setCompressedPayload(true);
	}

	return toReturn;

    }

}
